#include "utf8.h"

static inline bool within (unsigned char value, unsigned char lower, unsigned char greater) {
  return lower<=value && value<=greater;
}
static bool ascii_char (char* source) {
  return ((signed char)source[0]) >0;
}
static bool utf8_2bytes_char (char* source) {
  char a = source[0], b = source[1];
  return within(a,0xC2,0xDF) && within(b,0x80,0xBF);
}
static bool utf8_3bytes_char (char* source) {
  char a = source[0], b = source[1], c = source[2];
  return
    (a==0xE0             && within(b,0xA0,0xBF) && within(c,0x80,0xBF)) ||
    (within(a,0xE1,0xEC) && within(b,0x80,0xBF) && within(c,0x80,0xBF)) ||
    (a==0xED             && within(b,0x80,0x9F) && within(c,0x80,0xBF)) ||
    (within(a,0xEE,0xEF) && within(b,0x80,0xBF) && within(c,0x80,0xBF));
}
static bool utf8_4bytes_char (char* source) {
  char a = source[0], b = source[1], c = source[2], d = source[3];
  return
    (a==0xF0             && within(b,0x90,0xBF) && within(c,0x80,0xBF) && within(d,0x80,0xBF)) ||
    (within(a,0xF1,0xF3) && within(b,0x80,0x8F) && within(c,0x80,0xBF) && within(d,0x80,0xBF)) ||
    (a==0xF4             && within(b,0x80,0x8F) && within(c,0x80,0xBF) && within(d,0x80,0xBF));
}
bool uchar_valid (char* source) {
  return ascii_char(source) || utf8_2bytes_char(source) ||
    utf8_3bytes_char(source) || utf8_4bytes_char(source);
}
size_t uchar_bytes (char* source) {
  return ascii_char(source) ? 1 :
    utf8_2bytes_char(source) ? 2 :
    utf8_3bytes_char(source) ? 3 :
    utf8_4bytes_char(source) ? 4 : 0;
}
size_t ustring_length (char* source) {
  size_t length = 0, a, p=0;
  do {
    a = uchar_bytes(source+p);
    p += a;
    if (a) length ++;
  } while (a);
  return length;
}
size_t ustring_bytes (char* source) {
  size_t a, p=0;
  do {
    a = uchar_bytes(source+p);
    p += a;
  } while (a);
  return p;
}
size_t cstring_bytes (struct uchar* source) {
  size_t a = 0;
  while(source->bytes) {
    a += source->bytes;
    source++;
  }
  return a;
}
struct uchar next_uchar (char* source) {
  size_t bytes = uchar_bytes(source);
  if (bytes == 0) return (struct uchar){0};
  struct uchar uc = (struct uchar){
    .bytes=bytes,
    .chars[0]=source[0],
    .chars[1]=(bytes>=2 ? source[1] : 0),
    .chars[2]=(bytes>=3 ? source[2] : 0),
    .chars[3]=(bytes==4 ? source[3] : 0)
  };
  return uc;
}
void c_to_ustring (char* source, struct uchar* destination) {
  char *p = source;
  struct uchar uc, *us = destination;
  do {
    uc = next_uchar(p);
    p += uc.bytes;
    *us = uc;
    us ++;
  } while (uc.bytes);
}
void u_to_cstring (struct uchar* source, char* destination) {
  struct uchar *us = source;
  char *p = destination;
  while (us->bytes) {
    memcpy(p,us->chars,us->bytes);
    p += us->bytes;
    us ++;
  }
  p[0] = '\0';
}
size_t uchar_puts (int fileno, struct uchar *uc) {
  return write(fileno, uc->chars, uc->bytes);
}
size_t ustring_puts (int fileno, struct uchar *ustring) {
  size_t written = 0;
  while (ustring->bytes != 0) {
    size_t a = uchar_puts(fileno,ustring);
    if (a < ustring->bytes) break;
    written+=a;
    ustring++;
  }
  return written;
}
